Repository navigation
fix(runtime): the environment-membership check judges the principal the request resolved - #31396
Conversation
…he request resolved On a multi-environment host the dispatcher's environment-membership check now judges the principal the request resolved, whichever credential admitted it. A principal an OAuth access token or an API key admitted is judged as the token's user or the key's owner, in the organization its resolution vetted, including when a session cookie for another user is sent beside it. A session principal is judged as before. Clause-②: no Metadata-protocol: none Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5mgePB9NQ4PNhvYVxk
|
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ Contract-review-claim: session_01FEkQ5o8PE7ZtVjcor51wBZ · head bb57aaf · 2026-11-08T05:58Z |
📓 Docs Drift CheckThis PR changes 1 package(s): 11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 28 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4f3c1d8acec129c9d45dd243442e65c93535d09f && git checkout 4f3c1d8acec129c9d45dd243442e65c93535d09f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 405a78eae9311598f8868fe5bff92a2e17520f68 44a099f055c854599584b254e08b3f65ecc839e7 && git checkout -B drift-repro 405a78eae9311598f8868fe5bff92a2e17520f68 && git merge --no-ff 44a099f055c854599584b254e08b3f65ecc839e7
node scripts/docs-audit/affected-docs.mjs --json 405a78eae9311598f8868fe5bff92a2e17520f68
|
|
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ Contract reviewServed-tier: Inputs: the PR body, its one commit and message, the three-path net diff against Check-runs on the head at the time of writing: 47 runs, 42 completed (29 ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe narrowing was read against the published spec on
Implemented-by: VERDICT: PASS Generated by Claude Code |
…ad one admitting-credential kind `admittingCredentialKind` (runtime security) is the one reading of which credential admitted a request's caller: `oauth` when the envelope carries OAuth scopes, else the identity step's recorded admission. The environment-membership check and `apiForwardCredentialKind` both read it, so the two cannot disagree. Adds a pin for a principal an API key admitted on a scoped data route. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FEkQ5mgePB9NQ4PNhvYVxk
|
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ Contract-review-claim: session_01FEkQ5o8PE7ZtVjcor51wBZ · head 44a099f · 2026-11-08T06:51Z |
|
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ Contract reviewServed-tier: This is a DELTA round on the round-1 record, which judged head Check-runs on the new head at the time of writing: 49 runs, 34 completed (18 ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgmentThe narrowing itself is the one the round-1 record judged, on the same spec on
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ 席位意见:接受。QA 第三轮发现的安全加固(细节按披露裁决不公开)。在多环境宿主上,以 API key 或 OAuth 令牌进入的调用方,按其实际身份判断是否为所选环境的成员,与会话进入的调用方同一规则。契约复审与增量复审均 PASS,无必改项。 席位核对 — ACCEPT ·
|
Requested by Leehom · project thread
Refs #28284
Clause-②: no
Metadata-protocol: none
维护者速读
403 PROJECT_MEMBERSHIP_REQUIRED)现在判断的是请求解析出的主体。由 API key 或 OAuth access token 接纳的主体,按 key 的所有者或 token 的用户判断(同时带了别人 cookie 也一样),平台组织豁免使用解析时核验过的组织。由 session cookie 接纳的主体,判断方式与原来完全相同。@objectstack/runtime(patch)。单环境主机,以及关闭了该检查的主机(api.enforceProjectMembership: false)不受影响。会被新拒绝的只有:多环境主机上,以 API key 或 OAuth token 访问某环境作用域路由、但不是该环境成员的调用方。回滚即 revert 本 PR。packages/runtime/src/http-dispatcher.ts的enforceProjectMembership,session 读取之后按admittingCredentialKind新增的分支;以及新文件packages/runtime/src/security/admitting-credential.ts。pnpm --filter @objectstack/runtime exec vitest run src/http-dispatcher-membership-resolved-principal.test.ts,预期 8/8。What changes
admittingCredentialKind(request)(packages/runtime/src/security/admitting-credential.ts) is the one reading of which credential admitted a request's caller.oauthwhen the resolved context carries OAuth scopes.api_keyorsession.undefinedwhen nothing was recorded.apiForwardCredentialKind(action execution) now delegates to it, so the api-action forward and the membership check cannot disagree.HttpDispatcher.enforceProjectMembership: when that reading isapi_keyoroauth, the check judges the principalresolveRequestScoperesolved.executionContext.userId: the key's owner or the token's user. This holds even when a session cookie for another user rides beside it.executionContext.tenantId, the organization the resolution vetted.kernel-resolver, so noenvironmentId);Why
QA 第三轮发现的安全加固(细节按披露裁决不公开)
Verification
Readings on head
44a099f05: two commits onmainatc2bc76e3e. The second answers the contract review: one shared credential reader, and a pin on a scoped data route.Pins, red-first and restore
packages/runtime/src/http-dispatcher-membership-resolved-principal.test.ts(8): a multi-tenant host with a stub resolver andsys_environment_member, driven throughdispatch()./mcp;/mcp;/data/task.http-dispatcher.tsset back to itsmainblob, the file read 6 failed | 2 passed (8).Package suite and typecheck
tsc --noEmitinpackages/runtime: exit 0.pnpm --filter @objectstack/runtime testunder the verify lock: Test Files 556 passed (556), Tests 8965 passed | 19 skipped (8984),VERDICT command-exit 0.domains/meta-read-principal-filtering.measurement.test.tscaught it, because the two reads can disagree for a session principal.Derived gates
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderives 69 families on this diff. Every one ran and exited 0;check:type-check-debtran under the verify lock.--ranreconciliation: 69 derived, 69 run, 0 NOT-MEASURED, 0 UNRUN.eslint --no-inline-configon the changed source files: exit 0.Not measured:
kernel-resolverships in this repository, so the pins drivedispatch()with a stub resolver.environmentId, so they never reach this check.Human Minimum
packages/specholds no runtime logic. (Nopackages/specchange.)eslint --no-inline-configon the changed files: exit 0.main, one pull request.packages/spectouched: not touched.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code